package com.nebula.core.client;

import cn.hutool.core.collection.CollectionUtil;
import com.google.common.collect.Lists;
import com.nebula.core.constant.Constant;
import com.nebula.core.dsl.NebulaCypherRenderer;
import com.nebula.core.dsl.builder.FetchStatementBuilder;
import com.nebula.core.dsl.builder.FindPathStatementBuilder;
import com.nebula.core.dsl.builder.GetSubgraphStatementBuilder;
import com.nebula.core.dsl.builder.GoStatementBuilder;
import com.nebula.core.dsl.operation.NgqlBuilder;
import com.nebula.core.entity.vo.Edge;
import com.nebula.core.entity.vo.FieldDesc;
import com.nebula.core.entity.vo.MetaInfo;
import com.nebula.core.entity.vo.Vertex;
import com.nebula.core.util.NebulaFormat;
import com.vesoft.nebula.client.graph.data.ResultSet;
import lombok.extern.slf4j.Slf4j;
import org.neo4j.cypherdsl.core.Statement;
import org.neo4j.cypherdsl.core.renderer.Renderer;

import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author zhangtao
 */
@Slf4j
public class NebulaTemplate {

    private final Renderer cypherRenderer = NebulaCypherRenderer.create();

    private NebulaSession nebulaSession;

    public NebulaTemplate(NebulaSession nebulaSession) {
        this.nebulaSession = nebulaSession;
    }

    /**
     * 插入单个顶点
     *
     * @param entity
     * @return
     */
    public boolean insertOneVertex(Object entity) {
        return nebulaSession.execute(NgqlBuilder.insertOneVertex(entity));
    }

    /**
     * 插入单个顶点
     *
     * @param tagName
     * @param vertex
     * @return
     */
    public boolean insertOneVertex(String tagName, Vertex vertex) {
        return nebulaSession.execute(NgqlBuilder.insertOneVertex(tagName, vertex));
    }

    /**
     * 批量插入多个顶点
     *
     * @param tagName
     * @param vertexList
     * @return
     */
    public boolean insertManyVertex(String tagName, List<Vertex> vertexList) {
        if (CollectionUtil.isEmpty(vertexList)) {
            return false;
        }
        AtomicReference<Boolean> flag = new AtomicReference<>(true);
        if (vertexList.size() <= Constant.MAX_SIZE) {
            return nebulaSession.execute(NgqlBuilder.insertManyVertex(tagName, vertexList));
        } else {
            List<List<Vertex>> partition = Lists.partition(vertexList, 5000);
            partition.stream().forEach(list -> {
                flag.set(flag.get() & nebulaSession.execute(NgqlBuilder.insertManyVertex(tagName, list)));
            });
        }
        return flag.get();
    }

    /**
     * 批量插入多个顶点
     *
     * @param vertexList
     * @return
     */
    public boolean insertManyVertex(List<Object> vertexList) {
        if (CollectionUtil.isEmpty(vertexList)) {
            return false;
        }
        AtomicReference<Boolean> flag = new AtomicReference<>(true);
        if (vertexList.size() <= Constant.MAX_SIZE) {
            return nebulaSession.execute(NgqlBuilder.insertManyVertex(vertexList));
        } else {
            List<List<Object>> partition = Lists.partition(vertexList, 5000);
            partition.stream().forEach(list -> {
                flag.set(flag.get() & nebulaSession.execute(NgqlBuilder.insertManyVertex(list)));
            });
        }
        return flag.get();
    }

    /**
     * 删除顶点
     *
     * @param vids
     * @return
     */
    public boolean deleteVertex(String... vids) {
        return nebulaSession.execute(NgqlBuilder.deleteVertex(vids));
    }

    /**
     * 更新顶点
     *
     * @param tagName
     * @param vertex
     * @return
     */
    public boolean updateVertex(String tagName, Vertex vertex) {
        return nebulaSession.execute(NgqlBuilder.updateVertex(tagName, vertex));
    }

    /**
     * 更新顶点
     *
     * @param entity
     * @return
     */
    public boolean updateVertex(Object entity) {
        return nebulaSession.execute(NgqlBuilder.updateVertex(entity));
    }

    public boolean insertOneEdge(Object entity) {
        return nebulaSession.execute(NgqlBuilder.insertOneEdge(entity));
    }

    /**
     * 插入单条边
     *
     * @param edgeType
     * @param edge
     * @return
     */
    public boolean insertOneEdge(String edgeType, Edge edge) {
        return nebulaSession.execute(NgqlBuilder.insertOneEdge(edgeType, edge));
    }

    /**
     * 批量插入多条边
     *
     * @param edgeType
     * @param edgeList
     * @return
     */
    public boolean insertManyEdge(String edgeType, List<Edge> edgeList) {
        if (CollectionUtil.isEmpty(edgeList)) {
            return false;
        }
        AtomicReference<Boolean> flag = new AtomicReference<>(true);
        if (edgeList.size() <= Constant.MAX_SIZE) {
            return nebulaSession.execute(NgqlBuilder.insertManyEdge(edgeType, edgeList));
        } else {
            List<List<Edge>> partition = Lists.partition(edgeList, Constant.MAX_SIZE);
            partition.stream().forEach(list -> {
                flag.set(flag.get() & nebulaSession.execute(NgqlBuilder.insertManyEdge(edgeType, list)));
            });
        }
        return flag.get();
    }

    /**
     * 批量插入多条边
     *
     * @param edgeList
     * @return
     */
    public boolean insertManyEdge(List<Object> edgeList) {
        if (CollectionUtil.isEmpty(edgeList)) {
            return false;
        }
        AtomicReference<Boolean> flag = new AtomicReference<>(true);
        if (edgeList.size() <= Constant.MAX_SIZE) {
            return nebulaSession.execute(NgqlBuilder.insertManyEdge(edgeList));
        } else {
            List<List<Object>> partition = Lists.partition(edgeList, Constant.MAX_SIZE);
            partition.stream().forEach(list -> {
                flag.set(flag.get() & nebulaSession.execute(NgqlBuilder.insertManyEdge(list)));
            });
        }
        return flag.get();
    }

    /**
     * 删除边
     *
     * @param edgeType
     * @param edge
     * @return
     */
    public boolean deleteEdge(String edgeType, Edge... edge) {
        return nebulaSession.execute(NgqlBuilder.deleteEdge(edgeType, edge));
    }


    /**
     * 删除边
     *
     * @param edgeList
     * @return
     */
    public boolean deleteEdge(List<Object> edgeList) {
        if (CollectionUtil.isEmpty(edgeList)) {
            return false;
        }
        return nebulaSession.execute(NgqlBuilder.deleteEdge(edgeList));
    }

    /**
     * 删除边
     *
     * @param entity
     * @return
     */
    public boolean deleteEdge(Object entity) {
        Edge edge = NgqlBuilder.entityToEdge(entity);
        return nebulaSession.execute(NgqlBuilder.deleteEdge(edge.getEdgeType(), edge));
    }

    /**
     * 更新边
     *
     * @param edgeType
     * @param edge
     * @return
     */
    public boolean updateEdge(String edgeType, Edge edge) {
        return nebulaSession.execute(NgqlBuilder.updateEdge(edgeType, edge));
    }

    /**
     * 更新边
     *
     * @param entity
     * @return
     */
    public boolean updateEdge(Object entity) {
        Edge edge = NgqlBuilder.entityToEdge(entity);
        return nebulaSession.execute(NgqlBuilder.updateEdge(edge.getEdgeType(), edge));
    }

    /**
     * Go 查询
     *
     * @return
     */
    public GoStatementBuilder.GoStart go() {
        return NgqlBuilder.go(nebulaSession);
    }

    /**
     * fetch属性查询
     *
     * @return
     */
    public FetchStatementBuilder.FetchType fetch() {
        return NgqlBuilder.fetch(nebulaSession);
    }

    /**
     * findpath 路径查询
     *
     * @return
     */
    public FindPathStatementBuilder.FindPathType findPath() {
        return NgqlBuilder.findPath(nebulaSession);
    }

    /**
     * getSubgraph子图查询
     *
     * @return
     */
    public GetSubgraphStatementBuilder.GetSubgraphStart getSubgraph() {
        return NgqlBuilder.getSubgraph(nebulaSession);
    }

    /**
     * 查询Tag的详细信息
     *
     * @param tag
     * @return
     */
    public List<MetaInfo> describeTag(String tag) throws UnsupportedEncodingException {
        return NebulaFormat.getMetaInfo(nebulaSession.query(NgqlBuilder.describeTag(tag)));
    }

    /**
     * 查询Tag的创建信息
     *
     * @return
     */
    public String showCreateTag(String tag) throws UnsupportedEncodingException {
        return NebulaFormat.getCreateInfo(nebulaSession.query(NgqlBuilder.showCreateTag(tag)));
    }

    /**
     * 查询所有Tag列表
     *
     * @return
     */
    public List<String> showTags() throws UnsupportedEncodingException {
        return NebulaFormat.getTagsOrEdges(nebulaSession.query(NgqlBuilder.showTags()));
    }

    /**
     * 创建Tag
     *
     * @return
     */
    public boolean createTag(String tagName, List<FieldDesc> fieldDescList) {
        return nebulaSession.execute(NgqlBuilder.createTag(tagName, fieldDescList));
    }

    /**
     * 删除Tag
     *
     * @param tagName
     * @return
     */
    public boolean dropTag(String tagName) {
        return nebulaSession.execute(NgqlBuilder.dropTag(tagName));
    }

    /**
     * 更新Tag
     *
     * @param tagName
     * @return
     */
    public boolean alterTag(String tagName, List<FieldDesc> fieldDescList) {
        return nebulaSession.execute(NgqlBuilder.alterTag(tagName, fieldDescList));
    }

    /**
     * 查询Edge的详细信息
     *
     * @param edge
     * @return
     */
    public List<MetaInfo> describeEdge(String edge) throws UnsupportedEncodingException {
        return NebulaFormat.getMetaInfo(nebulaSession.query(NgqlBuilder.describeEdge(edge)));
    }

    /**
     * 查询Edge的创建信息
     *
     * @return
     */
    public String showCreateEdge(String edge) throws UnsupportedEncodingException {
        return NebulaFormat.getCreateInfo(nebulaSession.query(NgqlBuilder.showCreateEdge(edge)));
    }

    /**
     * 查询所有Edge列表
     *
     * @return
     */
    public List<String> showEdges() throws UnsupportedEncodingException {
        return NebulaFormat.getTagsOrEdges(nebulaSession.query(NgqlBuilder.showEdges()));
    }

    /**
     * 创建Edge
     *
     * @return
     */
    public boolean createEdge(String edgeType, List<FieldDesc> fieldDescList) {
        return nebulaSession.execute(NgqlBuilder.createEdge(edgeType, fieldDescList));
    }

    /**
     * 删除Edge
     *
     * @param edgeType
     * @return
     */
    public boolean dropEdge(String edgeType) {
        return nebulaSession.execute(NgqlBuilder.dropEdge(edgeType));
    }

    /**
     * 更新Edge
     *
     * @param edgeType
     * @return
     */
    public boolean alterEdge(String edgeType, List<FieldDesc> fieldDescList) {
        return nebulaSession.execute(NgqlBuilder.alterEdge(edgeType, fieldDescList));
    }

    /**
     * 查询
     *
     * @param statement
     * @return
     */
    public ResultSet query(String statement) {
        return nebulaSession.query(statement);
    }

    /**
     * 查询
     *
     * @param statement
     * @return
     */
    public ResultSet query(Statement statement) {
        return nebulaSession.query(cypherRenderer.render(statement));
    }

    /**
     * @param statement
     * @return
     */
    public boolean execute(String statement) {
        return nebulaSession.execute(statement);
    }

    /**
     * @param statement
     * @return
     */
    public boolean execute(Statement statement) {
        return nebulaSession.execute(cypherRenderer.render(statement));
    }


    /**
     * 根据vid判断点是否存在
     *
     * @param vid
     * @return
     */
    public boolean nodeExist(String vid) {
        Statement statement = NgqlBuilder.findNodeByVid(vid);
        ResultSet resultSet = nebulaSession.query(cypherRenderer.render(statement));
        return !resultSet.isEmpty();
    }

    /**
     * 根据vid判断边是否存在
     *
     * @param srcId
     * @param dstId
     * @return
     */
    public boolean edgeExist(String srcId, String dstId) {
        Statement statement = NgqlBuilder.findEdgeByVid(srcId, dstId);
        ResultSet resultSet = nebulaSession.query(cypherRenderer.render(statement));
        return !resultSet.isEmpty();
    }

}
